﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using TSites.Service.Entities;
using TSites.Service.Interfaces;

namespace TSites.Service
{
	public class XmlCompanyProcessor : BasedFileProcessor<Company>
	{
		#region Const XML elements

		private const string ROOT = "companies";
		private const string COMPANY = "company";
		private const string ID = "id";
		private const string TAX_CODE = "taxcode";
		private const string NAMES = "names";
		private const string VNNAME = "vnname";
		private const string ENNAME = "enname";
		private const string CONTRACTS = "contracts";
		private const string CONTRACT_NO = "contractno";
		private const string REPRESENTED_BY = "representedby";
		private const string POSITION = "position";
		private const string VNPOSITION = "vnposition";
		private const string ENPOSITION = "enposition";
		private const string PHONE = "phone";
		private const string FAX = "fax";
		private const string ADDRESSES = "addresses";
		private const string VNADDRESS = "vnaddress";
		private const string ENADDRESS = "enaddress";

		#endregion

		public override List<Company> Read(string path)
		{
			List<Company> companyList = null;
			if (File.Exists(path))
			{
				FileStream fStream = null;
				XmlReader xR = null;
				try
				{
					// take xmlReaderSettings to remove white space.
					XmlReaderSettings settings = new XmlReaderSettings();
					settings.IgnoreWhitespace = true;
					settings.CloseInput = true;

					fStream = new FileStream(path, FileMode.Open, FileAccess.Read);
					xR = XmlReader.Create(path, settings);

					companyList = new List<Company>(256);
					Company com = null;
					while (xR.Read())
					{
						// since xmlReader does not give handle on the node/attribute, we have to determine with 
						// the help of NodeType. I have mentioned possible nodetypes being used in our xml file. 
						switch (xR.NodeType)
						{
							case XmlNodeType.Element:
								switch (xR.Name)
								{
									case COMPANY:
										com = new Company { ContractNos = new List<string>() };
										break;

									case ID: // move to its value
										xR.Read();
										com.ID = xR.Value;
										break;

									case TAX_CODE: // move to its value
										xR.Read();
										com.TaxCode = xR.Value;
										break;

									case VNNAME:
										xR.Read(); // move to its value
										com.VietnamesName = xR.Value;
										break;

									case ENNAME: // move to its value
										xR.Read();
										com.EnglishName = xR.Value;
										break;

									case CONTRACT_NO: // move to its value
										xR.Read();
										com.ContractNos.Add(xR.Value);
										break;

									case REPRESENTED_BY: // move to its value
										xR.Read();
										com.RepresentedBy = xR.Value;
										break;

									case VNPOSITION: // move to its value
										xR.Read();
										com.VietnamesPosition = xR.Value;
										break;

									case ENPOSITION: // move to its value
										xR.Read();
										com.EnglishPosition = xR.Value;
										break;

									case PHONE: // move to its value
										xR.Read();
										com.Phone = xR.Value;
										break;

									case FAX: // move to its value
										xR.Read();
										com.Fax = xR.Value;
										break;

									case VNADDRESS: // move to its value
										xR.Read();
										com.VietnamesAddress = xR.Value;
										break;

									case ENADDRESS: // move to its value
										xR.Read();
										com.EnglishAddress = xR.Value;
										break;

									case NAMES: // do nothing
									case ADDRESSES:
									default:
										break;
								}
								break;

							case XmlNodeType.EndElement:
								if (string.Equals(xR.Name, COMPANY))
								{
									companyList.Add(com);
								}
								break;

							default:
								break;
						}
					}
				}
				catch (Exception)
				{
					throw;
				}
				finally
				{
					if (xR != null)
					{
						xR.Close();
					}
					if (fStream != null)
					{
						fStream.Close();
					}
				}
			}

			return companyList;
		}

		public override void Write(string path, Company[] content)
		{
			FileStream fStream = null;
			XmlWriter xW = null;
			try
			{
				XmlWriterSettings settings = new XmlWriterSettings();
				settings.Encoding = System.Text.Encoding.UTF8;
				settings.CloseOutput = true;
				// settings.Indent = true; //indenting would increase the file size. It went beyond 2 gigs.

				fStream = new FileStream(path, FileMode.Create, FileAccess.Write);
				xW = XmlWriter.Create(fStream, settings);
				//write start element in the new xml file using xmlWriter.
				xW.WriteStartDocument();
				xW.WriteStartElement(ROOT);

				foreach (Company com in content)
				{
					xW.WriteStartElement(COMPANY);
					WriteElement(xW, ID, com.ID);
					WriteElement(xW, TAX_CODE, com.TaxCode);

					xW.WriteStartElement(NAMES);
					WriteElement(xW, VNNAME, com.VietnamesName);
					WriteElement(xW, ENNAME, com.EnglishName);
					xW.WriteEndElement();

					xW.WriteStartElement(CONTRACTS);
					if (com.ContractNos != null)
					{
						foreach (string item in com.ContractNos)
						{
							WriteElement(xW, CONTRACT_NO, item);
						}
					}
					xW.WriteEndElement();

					WriteElement(xW, REPRESENTED_BY, com.RepresentedBy);
					xW.WriteStartElement(POSITION);
					WriteElement(xW, VNPOSITION, com.VietnamesPosition);
					WriteElement(xW, ENPOSITION, com.EnglishPosition);
					xW.WriteEndElement();

					WriteElement(xW, PHONE, com.Phone);
					WriteElement(xW, FAX, com.Fax);

					xW.WriteStartElement(ADDRESSES);
					WriteElement(xW, VNADDRESS, com.VietnamesAddress);
					WriteElement(xW, ENADDRESS, com.EnglishAddress);
					xW.WriteEndElement();

					xW.WriteEndElement();
				}

				xW.WriteEndElement();
			}
			catch (Exception)
			{
				throw;
			}
			finally
			{
				if (xW != null)
				{
					xW.Close();
				}
				if (fStream != null)
				{
					fStream.Close();
				}
			}
		}

		private void WriteElement(XmlWriter xWriter, string elementName, object elementValue)
		{
			xWriter.WriteStartElement(elementName);
			elementValue = elementValue ?? string.Empty;
			xWriter.WriteValue(elementValue);
			xWriter.WriteEndElement();
		}
	}
}
